/*
 * Tree.h
 *
 *  Created on: 22 févr. 2014
 *      Author: Raphaël
 */

#ifndef BINARYTREE_H_
#define BINARYTREE_H_

#include <memory>
#include <iostream>
#include <string>
#include "Tree.hpp"
#include "raphTools/Object.h"
#include "raphTools/Tools.h"

using namespace raph;
using namespace raph::tools;

template<typename T> class Node
    {
    private:
        /*-------------------------------------------------------------------
         private attributs
         -------------------------------------------------------------------*/
        std::shared_ptr<T> m_ptrData;
        std::shared_ptr<Node<T>> m_ptrLeftNode;
        std::shared_ptr<Node<T>> m_ptrRightNode;

    public:
        /*-------------------------------------------------------------------
         constructor/destructor
         -------------------------------------------------------------------*/
        Node() :
                m_ptrData(nullptr), m_ptrLeftNode(nullptr), m_ptrRightNode(nullptr)
            {
            std::cout << "node construction" << std::endl;
            }

        virtual ~Node()
            {
            std::cout << "node destruction" << std::endl;
            }

        /*-------------------------------------------------------------------
         public methods
         -------------------------------------------------------------------*/
        /*-------------------------------
         get
         -------------------------------*/
        std::shared_ptr<T>& getData()
            {
            return m_ptrData;
            }

        std::shared_ptr<Node<T>>& getLeftNode()
            {
            return m_ptrLeftNode;
            }

        std::shared_ptr<Node<T>>& getRightNode()
            {
            return m_ptrRightNode;
            }

        void setData(const T &data)
            {
            m_ptrData.reset(new T);
            *m_ptrData = data;
            }

    };

template<typename T> class BinaryTree: public Tree<T>, public Object
    {
    private:
        /*-------------------------------------------------------------------
         private attributs
         -------------------------------------------------------------------*/
        std::shared_ptr<Node<T>> m_ptrRoot;
        int m_nbModes;
        std::weak_ptr<Node<T>> m_currentNode;

    public:
        /*-------------------------------------------------------------------
         constructor/destructor
         -------------------------------------------------------------------*/
        BinaryTree() :
                m_ptrRoot(nullptr), m_nbModes(0)
            {
            std::cout << "tree construction" << std::endl;
            }
        virtual ~BinaryTree()
            {
            std::cout << "tree destruction" << std::endl;
            }

        /*-------------------------------------------------------------------
         public methods
         -------------------------------------------------------------------*/
        std::string toString() override
            {
            std::list<T> list;
            this->__read__(m_ptrRoot, list);
            std::string str;
            str.append("[");
            for (T &data : list)
                {
                std::string tmp(Convert::toString(data));
                str.append(tmp);
                str.append(", ");
                }
            str.append("]");

            return str;
            }

        BinaryTree<T>& operator<<(const T &data)
            {
            this->__insert__(m_ptrRoot, data);
            return *this;
            }

        void insert(const T &data) override
            {
            this->__insert__(m_ptrRoot, data);
            }

        void remove(const T &data) override
            {
            this->__remove__(m_ptrRoot, data);
            }

        std::list<T> toList() override
            {
            std::list<T> list;
            this->__read__(m_ptrRoot, list);
            return list;
            }

    private:
        /*-------------------------------------------------------------------
         private methods
         -------------------------------------------------------------------*/
        void __read__(const std::shared_ptr<Node<T>> &ptrNode, std::list<T> &list)
            {
            if (ptrNode.get())
                {
                if (ptrNode->getLeftNode().get())
                    {
                    this->__read__(ptrNode->getLeftNode(), list);
                    }

                if (ptrNode->getData().get())
                    {
                    list.push_back(*ptrNode->getData());
                    }

                if (ptrNode->getRightNode().get())
                    {
                    this->__read__(ptrNode->getRightNode(), list);
                    }
                }
            }

        void __insert__(std::shared_ptr<Node<T>> &ptrNode, const T &data)
            {
            if (ptrNode.get())
                {
                if (*ptrNode->getData() > data)
                    {
                    __insert__(ptrNode->getLeftNode(), data);
                    }
                else
                    {
                    __insert__(ptrNode->getRightNode(), data);
                    }
                }
            else
                {
                ptrNode.reset(new Node<T>);
                ptrNode->setData(data);
                ++m_nbModes;
                }
            }

        void __remove__(std::shared_ptr<Node<T>> &ptrNode, const T &data)
            {
            if (ptrNode.get())
                {
                if (*ptrNode->getData() > data)
                    {
                    __remove__(ptrNode, data);

                    }
                }
            }
    };

#endif /* TREE_H_ */
